﻿Public Class Report
    Inherits System.Web.UI.Page

    Private ReportInfo As RdlViewer.clsReportInfo
    Private ReportDir As String = "~/Reports"

    Private Function ReportDirLang() As String
        'reports are stored in folder named for language; by default, are English, other languages are appended by language code
        If ProjectSettings.Language.TwoLetterISOLanguageName = "en" Then
            Return ReportDir
        Else
            Return ReportDir & "-" & ProjectSettings.Language.TwoLetterISOLanguageName
        End If
    End Function

    Protected Overrides Sub InitializeCulture()
        With ProjectSettings
            System.Threading.Thread.CurrentThread.CurrentUICulture = .Language
            System.Threading.Thread.CurrentThread.CurrentCulture = .Language
        End With

        MyBase.InitializeCulture()
    End Sub

    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        If Not IsPostBack Then

            FillReportList()
            lblInfo.Text = String.Format(lblInfo.Text, NumSelectedRecords)

        Else

            ReportInfo = Session("ReportInfo")
            If ReportInfo IsNot Nothing AndAlso ReportInfo.ReportParameters.Count > 0 Then AddParameterControls()
        End If

    End Sub

    ''' <summary>
    ''' Get names of reports in directory; should use long descriptive names
    ''' </summary>
    ''' <remarks>Can restrict reports to certain projects by preceding name with project name and then ! (e.g., APOT!Report Name.rdl)</remarks>
    Private Sub FillReportList()
        For Each fn As String In My.Computer.FileSystem.GetFiles(MapPath(ReportDirLang), FileIO.SearchOption.SearchTopLevelOnly, {"*.rdl*"})
            Dim filename As String = IO.Path.GetFileNameWithoutExtension(fn)
            If filename.Contains("!") Then 'should only be displayed if project name matches
                If Project Is Nothing Then Response.Redirect("Configure.aspx")
                If Project.Name.Equals(filename.Split("!")(0), StringComparison.InvariantCultureIgnoreCase) Then
                    cboReports.Items.Add(filename.Split("!")(1))
                End If
            Else
                cboReports.Items.Add(filename)
            End If
        Next
    End Sub

    Private Function ReportName() As String
        Dim fn As String = cboReports.Text
        'if file exists, use it; if not, see if one exists preceded by project name and then use that
        If My.Computer.FileSystem.FileExists(MapPath(IO.Path.Combine(ReportDirLang, fn & ".rdl"))) Then
            Return fn 'use it
        ElseIf My.Computer.FileSystem.FileExists(MapPath(IO.Path.Combine(ReportDirLang, Project.Name & "!" & fn & ".rdl"))) Then
            Return Project.Name & "!" & fn
        Else
            Throw New ApplicationException("Unable to find selected report: " & fn)
            Return ""
        End If
    End Function

    Private Sub btnReports_Click(sender As Object, e As EventArgs) Handles btnReports.Click
        RunReport()
    End Sub

    Public Shared Function Join(FirstTable As DataTable, SecondTable As DataTable, FirstKeyColumn As DataColumn(), SecondKeyColumn As DataColumn()) As DataTable
        'Create Empty Table
        Dim table As New DataTable("Join")

        ' Use a DataSet to leverage DataRelation
        Using ds As New DataSet()

            'Add Copy of Tables
            ds.Tables.AddRange(New DataTable() {FirstTable.Copy(), SecondTable.Copy()})

            'Identify Joining Columns from First
            Dim parentcolumns As DataColumn() = New DataColumn(FirstKeyColumn.Length - 1) {}
            For i As Integer = 0 To parentcolumns.Length - 1
                parentcolumns(i) = ds.Tables(0).Columns(FirstKeyColumn(i).ColumnName)
            Next

            'Identify Joining Columns from Second
            Dim childcolumns As DataColumn() = New DataColumn(SecondKeyColumn.Length - 1) {}

            For i As Integer = 0 To childcolumns.Length - 1
                childcolumns(i) = ds.Tables(1).Columns(SecondKeyColumn(i).ColumnName)
            Next

            'Create DataRelation

            Dim r As New DataRelation(String.Empty, parentcolumns, childcolumns, False)
            ds.Relations.Add(r)

            'Create Columns for JOIN table

            For i As Integer = 0 To FirstTable.Columns.Count - 1
                table.Columns.Add(FirstTable.Columns(i).ColumnName, FirstTable.Columns(i).DataType)
            Next

            For i As Integer = 0 To SecondTable.Columns.Count - 1
                'Beware Duplicates
                If Not table.Columns.Contains(SecondTable.Columns(i).ColumnName) Then
                    table.Columns.Add(SecondTable.Columns(i).ColumnName, SecondTable.Columns(i).DataType)
                Else
                    table.Columns.Add(SecondTable.TableName & "_" & SecondTable.Columns(i).ColumnName, SecondTable.Columns(i).DataType)
                End If
            Next

            'Loop through First table

            table.BeginLoadData()

            For Each firstrow As DataRow In ds.Tables(0).Rows
                Dim parentarray As Object() = firstrow.ItemArray
                'Get "joined" rows
                Dim childrows As DataRow() = firstrow.GetChildRows(r)
                If childrows IsNot Nothing AndAlso childrows.Length > 0 Then
                    For Each secondrow As DataRow In childrows
                        Dim secondarray As Object() = secondrow.ItemArray
                        Dim joinarray As Object() = Nothing
                        joinarray = New Object(parentarray.Length + (secondarray.Length - 1)) {}
                        Array.Copy(parentarray, 0, joinarray, 0, parentarray.Length)
                        Array.Copy(secondarray, 0, joinarray, parentarray.Length, secondarray.Length)
                        table.LoadDataRow(joinarray, True)
                    Next
                Else 'if not matching records, just copy parent table records
                    Dim joinarray As Object() = New Object(parentarray.Length) {}
                    Array.Copy(parentarray, 0, joinarray, 0, parentarray.Length)
                    table.LoadDataRow(joinarray, True)
                End If
            Next
            table.EndLoadData()
        End Using

        Return table

    End Function

    Public Shared Function Join(FirstTable As DataTable, SecondTable As DataTable, FirstKeyColumn As DataColumn, SecondKeyColumn As DataColumn) As DataTable
        Return Join(FirstTable, SecondTable, New DataColumn() {FirstKeyColumn}, New DataColumn() {SecondKeyColumn})
    End Function

    Public Shared Function Join(FirstTable As DataTable, SecondTable As DataTable, FirstKeyColumn As String, SecondKeyColumn As String) As DataTable
        Return Join(FirstTable, SecondTable, New DataColumn() {FirstTable.Columns(FirstKeyColumn)}, New DataColumn() {FirstTable.Columns(SecondKeyColumn)})
    End Function

    Public Shared Function Join(FirstTable As DataTable, SecondTable As DataTable, KeyColumn As String) As DataTable
        Return Join(FirstTable, SecondTable, New DataColumn() {FirstTable.Columns(KeyColumn)}, New DataColumn() {FirstTable.Columns(KeyColumn)})
    End Function

    Public Shared Function Join(Dataset As DataSet, FirstTable As String, SecondTable As String, KeyColumn As String) As DataTable
        Return Join(Dataset.Tables(FirstTable), Dataset.Tables(SecondTable), KeyColumn, KeyColumn)
    End Function

    ''' <summary>
    ''' Report was selected in dropdown and Run Report button clicked; if is simple report without parms, show it; otherwise
    ''' will set hidden field value so that dialog appears prompting for parameters
    ''' </summary>
    ''' <remarks></remarks>
    Private Sub RunReport()

        'hide the instructions
        divInstructions.Visible = False
        'ReportViewer1.Visible = True

        'note that ReportViewer does not automatically bind and fill datasource, so report is inspected and bound here
        'see http://www.gotreportviewer.com for examples

        Dim fn As String = MapPath(IO.Path.Combine(ReportDirLang, ReportName() & ".rdl"))

        If ReportInfo Is Nothing OrElse ReportInfo.FileName <> fn Then
            ReportInfo = New RdlViewer.clsReportInfo(fn)
            ReportInfo.Initialize()
        End If

        'see if any of the report datasets reference Temp_WRDBData
        For Each ds In ReportInfo.DataSets
            If ds.Value.query.ToUpper.Contains("TEMP_WRDBDATA") Then 'must join

                CheckSelectedData()

                'having a selected data table and support tables in dataset doesn't really help much to replace the report dataset because
                'the report designer probably created a datatable which is the result of a database join operation; to simulate this,
                'just create a Temp_WRDBData table that contains all possible joined columns that a report might want

                'when GetData examines datasets, it will find the tablename and replace with dataset table

                '6/11/15--changed logic so that sql select statement is remembered and substituted in place of Temp_WRDBData when report run, so the dtJoin datatable is not used on the report page (but may be used on other pages)

                'Dim dtJoin As DataTable = Nothing

                'If ProjectDataset.Tables.Contains("Stations") AndAlso ProjectDataset.Tables.Contains("PCodes") AndAlso ProjectDataset.Tables.Contains("Criteria") Then
                '    'create subset of table with most popular columns to avoid huge joined table
                '    Dim dtStations As DataTable = ProjectDataset.Tables("Stations").DefaultView.ToTable(True, "Station_ID", "Station_Name", "Station_Type", "WaterBodyType")
                '    Dim dtPCodes As DataTable = ProjectDataset.Tables("PCodes").DefaultView.ToTable(True, "PCode", "Anal_Name", "Units", "Sig_Figs", "Det_Limit", "Min_Limit", "Max_Limit", "Min_Criteria", "Max_Criteria")
                '    Dim dtCCodes As DataTable = ProjectDataset.Tables("CCodes").DefaultView.ToTable(True, "CCode", "CCode_Name", "Description")
                '    Dim dtCriteria As DataTable = ProjectDataset.Tables("Criteria").DefaultView.ToTable(True, "PCode", "Station_Type", "Min_Criteria", "Max_Criteria")
                '    Dim dtSelected As DataTable = ProjectDataset.Tables("SelectedData")
                '    dtJoin = Join(dtSelected, dtStations, "Station_ID")
                '    dtJoin = Join(dtJoin, dtPCodes, "PCode")
                '    dtJoin = Join(dtJoin, dtCCodes, "CCode")
                '    dtJoin = Join(dtJoin, dtCriteria, "PCode", "Station_Type")

                '    'add two columns so can fill with appropriate criteria limits from PCode or Criteria table
                '    dtJoin.Columns.Add("MinCrit", GetType(System.Single))
                '    dtJoin.Columns.Add("MaxCrit", GetType(System.Single))
                '    For Each row In dtJoin.Rows
                '        If Not IsDBNull(row("Criteria_Min_Criteria")) Or Not IsDBNull(row("Criteria_Max_Criteria")) Then
                '            row("MinCrit") = row("Criteria_Min_Criteria")
                '            row("MaxCrit") = row("Criteria_Max_Criteria")
                '        Else 'use values from PCode table
                '            row("MinCrit") = row("Min_Criteria")
                '            row("MaxCrit") = row("Max_Criteria")
                '        End If
                '    Next
                'Else
                '    dtJoin = ProjectDataset.Tables("SelectedData")
                'End If

                'dtJoin.TableName = "Temp_WRDBData"
                'If ProjectDataset.Tables.Contains("Temp_WRDBData") Then ProjectDataset.Tables.Remove("Temp_WRDBData")
                'ProjectDataset.Tables.Add(dtJoin)

            End If
        Next

        If Not ProjectSettings.Load Then Response.Redirect("Configure.aspx") : Exit Sub

        Session("ReportInfo") = ReportInfo

        'if the selected report has parameters, add controls above the report viewer to prompt for the values
        'note: dynamically added controls must be added on each roundtrip

        If ReportInfo.ReportParameters.Count > 0 Then
            ReportViewer1.Reset()
            'first time selected report; refresh screen with parameter controls before try to run report
            'when page is reloaded, parameters will appear
            AddParameterControls()

            'RegisterScript(UpdatePanel1, "hideProgress();")
            ShowDialog(UpdatePanel1, pnlParameters, GetGlobalResourceObject("WRDB", "ReportParmTitle"), 750)

        Else
            'RegisterScript(UpdatePanel1, "hideProgress();")

            'note: the following is very similar to code found in frmReporting.RefreshLocalReport in WRDB 6.x
            With ReportViewer1
                .Reset()
                .ProcessingMode = Microsoft.Reporting.WebForms.ProcessingMode.Local
                With .LocalReport
                    .ReportPath = MapPath(IO.Path.Combine(ReportDirLang, ReportName() & ".rdl"))
                    GetData()
                    .Refresh()
                End With
            End With
        End If

    End Sub

    Private Sub AddParameterControls()

        ReportInfo = Session("ReportInfo")
        If ReportInfo Is Nothing OrElse ReportInfo.ReportParameters.Count = 0 Then Exit Sub

        For i As Integer = tblParms.Rows.Count - 1 To 0 Step -1
            tblParms.Rows.RemoveAt(i)
        Next

        'for each parameter in report add selection controls depending on the type of parameter

        For Each rp As RdlViewer.ReportParmInfo In ReportInfo.ReportParameters
            Dim cntl As Control = Nothing
            Dim cntlExt As Control = Nothing
            Dim isTextBox As Boolean = False
            Select Case rp.DataType
                Case "String", "Integer"
                    If rp.Datasets.Count + rp.Parameters.Count = 0 Then 'just use text box
                        isTextBox = True
                        cntl = New TextBox
                        If rp.MultiValue Then
                            With CType(cntl, TextBox)
                                .TextMode = TextBoxMode.MultiLine
                                .Wrap = False
                                .Style.Add("padding-top", "3px")
                                .Style.Add("padding-bottom", "3px")
                                .Style.Add("width", "30em")
                                .Style.Add("height", "100px")
                                If rp.Value IsNot Nothing Then
                                    .Text = rp.Value
                                ElseIf rp.Defaults.Count = 1 Then
                                    .Text = rp.Defaults(0)
                                End If
                            End With
                        End If
                    Else 'use combobox
                        Dim cbo As New DropDownList
                        For Each ds As RdlViewer.ParmDatasetInfo In rp.Datasets
                            For Each kv As KeyValuePair(Of String, String) In ds.fields
                                If ReportInfo.DataSets(ds.datasetName).dataTable Is Nothing Then
                                    ReportInfo.GetData(ds.datasetName, ProjectDataset)
                                End If
                                Dim view As New DataView(ReportInfo.DataSets(ds.datasetName).dataTable)
                                Dim dt As DataTable = view.ToTable(True, kv.Key)
                                For Each dr As DataRow In dt.Select("", kv.Key)
                                    Dim item As String = dr(kv.Key)
                                    cbo.Items.Add(New ListItem(item, item))
                                Next
                            Next
                        Next
                        For Each kv As KeyValuePair(Of String, String) In rp.Parameters
                            cbo.Items.Add(New ListItem(kv.Value, kv.Key))
                        Next

                        If rp.Value IsNot Nothing Then
                            cbo.Text = rp.Value
                        ElseIf rp.Defaults.Count > 0 Then
                            If rp.Parameters.Count > 0 Then
                                cbo.Text = rp.Parameters(rp.Defaults(0))
                            Else
                                cbo.Text = rp.Defaults(0)
                            End If
                        End If
                        cntl = cbo
                    End If
                Case "DateTime" 'use textbox with calendar extender
                    cntl = New TextBox
                    With CType(cntl, TextBox)
                        .Style.Add("padding-top", "0px")
                        .Style.Add("padding-bottom", "0px")
                        .Width = New Unit(10, UnitType.Em)
                        If rp.Value IsNot Nothing AndAlso IsDate(rp.Value) Then
                            .Text = CDate(rp.Value).ToString("d")
                        ElseIf rp.Defaults.Count = 1 Then
                            .Text = rp.Defaults(0)
                            If IsDate(.Text) Then
                                .Text = CDate(.Text).ToString("d")
                            Else
                                .Text = ""
                            End If
                        End If
                    End With
                    cntlExt = New AjaxControlToolkit.CalendarExtender
                    With CType(cntlExt, AjaxControlToolkit.CalendarExtender)
                        .ID = rp.Name & "_ext"
                        .Format = "d"
                        .TargetControlID = rp.Name
                    End With

            End Select
            cntl.ID = rp.Name

            Dim lbl As New Label
            With lbl
                .ID = rp.Name & "_lbl"
                .Style.Add("Padding-left", "1em")
                .Text = rp.Prompt
                If Not .Text.EndsWith(":") Then .Text &= ":"
                .Text = "    " & .Text.Trim & "  "
            End With

            Dim lit As New Literal
            With lit
                .Text = "<BR />"
            End With

            Dim tr As New TableRow
            Dim tc As New TableCell
            tc.Controls.Add(lbl)
            tr.Cells.Add(tc)
            tc = New TableCell
            tc.Controls.Add(cntl)
            If cntlExt IsNot Nothing Then tc.Controls.AddAt(1, cntlExt)
            tr.Cells.Add(tc)
            'tblParms.Rows.AddAt(tblParms.Rows.Count - 1, tr)
            tblParms.Rows.Add(tr)
        Next

    End Sub

    Private Function GetParameters() As Boolean
        ReportInfo = Session("ReportInfo")
        If ReportInfo Is Nothing OrElse ReportInfo.ReportParameters.Count = 0 Then Return True

        'retrieve selections from dynamic controls (and save as cookies for next time through)

        For Each parm As RdlViewer.ReportParmInfo In ReportInfo.ReportParameters
            With parm
                Dim cntl As Control = pnlParameters.FindControl(parm.Name)
                If cntl Is Nothing Then
                    Throw New ApplicationException("Unable to find control: " & .Name)
                ElseIf TypeOf cntl Is DropDownList Then
                    'note that the text field contains the value of the dropdown, not necessarily the displayed text
                    .Value = CType(cntl, DropDownList).Text
                ElseIf TypeOf cntl Is TextBox Then
                    .Value = CType(cntl, TextBox).Text
                Else
                    Throw New ApplicationException("Invalid control type found: " & .Name)
                End If

                If parm.Name Like "*Date*" AndAlso Not IsDate(.Value) Then Return False

                Dim reportparm As New Microsoft.Reporting.WebForms.ReportParameter(.Name, .Value, True)
                Try
                    ReportViewer1.LocalReport.SetParameters(reportparm)
                Catch ex As Exception
                    Debug.Print("Error setting parameters: " & ex.Message)
                End Try
            End With
        Next

        Session("ReportInfo") = ReportInfo

        'after retrieve parameters, remove all the controls to avoid JS error
        For i As Integer = tblParms.Rows.Count - 1 To 0 Step -1
            tblParms.Rows.RemoveAt(i)
        Next

        Return True
    End Function

    ''' <summary>
    ''' Fill datasets in report with data
    ''' </summary>
    ''' <remarks></remarks>
    Private Sub GetData()
        'select which report to display
        'note: the following is very similar to code found in frmReporting.RefreshLocalReport in WRDB 6.x
        With ReportViewer1.LocalReport
            .DataSources.Clear()
            For Each dsName As String In .GetDataSourceNames
                Dim dt As DataTable = Nothing
                Select Case dsName
                    Case "Metricos" 'use a custom routine to fill a datatable under program control to simplify the design in reportbuilder
                        dt = clsReportMetricos.GetData(Project, .GetParameters)
                    Case Else
                        dt = ReportInfo.GetData(dsName, .GetParameters, Project)
                End Select
                If dt IsNot Nothing Then
                    .DataSources.Add(New Microsoft.Reporting.WebForms.ReportDataSource(dsName, dt))
                End If
            Next
        End With
    End Sub

    Private Sub RefreshClicked(sender As Object, e As EventArgs) Handles btnRefresh.Click
        'btnRefresh is the OK button on the parameters dialog box; update the parameters and then hide the dialog box
        ReportInfo = Session("ReportInfo")
        If ReportInfo Is Nothing Then Response.Redirect("Configure.aspx") 'if session terminates or website restarts; goto configure screen
        With ReportViewer1
            .Reset()
            .ProcessingMode = Microsoft.Reporting.WebForms.ProcessingMode.Local
            With .LocalReport
                .ReportPath = MapPath(IO.Path.Combine(ReportDirLang, ReportName() & ".rdl"))
                If GetParameters() Then
                    GetData()
                    .Refresh() 'in case default values for parameters is OK (or no parameters)
                End If
            End With
        End With
    End Sub

    ''' <summary>
    ''' This PageMethod allows a resource value to be passed to the page so JavaScript can display message in correct language
    ''' </summary>
    <System.Web.Services.WebMethod()>
    Public Shared Function GetResource(key As String) As String
        Dim res As String = HttpContext.GetGlobalResourceObject("WRDB", key, ProjectSettings.Language)
        If String.IsNullOrEmpty(res) Then Throw New ApplicationException("Resource was not found: " & key) Else Return res
    End Function

    ''' <summary>
    ''' This PageMethod returns a boolean indicating whether the selected report has embedded parameters
    ''' </summary>
    <System.Web.Services.WebMethod()>
    Public Shared Function HasParameters() As Boolean
        Dim ReportInfo As RdlViewer.clsReportInfo
        ReportInfo = HttpContext.Current.Session("ReportInfo")
        Return Not (ReportInfo Is Nothing OrElse ReportInfo.ReportParameters.Count = 0)
    End Function

End Class